(* Copyright (c) 2018-2024 by Kithara Software GmbH. All rights reserved. *)

//##############################################################################################################
//
// File:         NetworkUdpSend_dll.dpr
//
// Used Modules: Network Module
//
// Description:  Sample kernel DLL for how to implement a UDP sender
//
// Creator:      a.thi 2018-12-21
//
//##############################################################################################################

   {*=====================================================================*\
   |                    *** DISCLAIMER OF WARRANTY ***                     |
   |                                                                       |
   |       THIS  CODE AND INFORMATION IS PROVIDED "AS IS"  WITHOUT         |
   |       A  WARRANTY OF  ANY KIND, EITHER  EXPRESSED OR  IMPLIED,        |
   |       INCLUDING  BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF         |
   |       MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.        |
   \*=====================================================================*}

//##############################################################################################################
//
// Purpose:
//
// This example shows how to send UDP datagrams.
//
// First we open the network adapter and create a socket.
// Then we send incoming keyboard characters to a target socket.
//
// This sample is designed to be the counterpart to the sample NetworkUdpReceive.
//
// PLEASE SPECIFY YOUR IP-ADDRESS, SUBNET-MASK AND GATEWAY-IP-ADDRESS IN THE KS_makeIPv4() CALLS AS WELL AS
// THE RECEIVER IP-ADRESS AND PORT IN THE APPLICATION PROGRAMM BEFORE RUNNING THIS EXAMPLE!
//
//##############################################################################################################

library NetworkUdpSend_dll;

{$APPTYPE CONSOLE}

uses
  _KitharaSmp in '..\_KitharaSmp\_KitharaSmp.pas',
  KrtsDemo in '..\..\dev\KrtsDemo.pas',
  NetworkUdpSend_inc in 'NetworkUdpSend_inc.pas';

var


//--------------------------------------------------------------------------------------------------------------
// Shared memory is used to share data between the DLL in kernel space and the application in user space.
//--------------------------------------------------------------------------------------------------------------

  _pSys : PSharedData = nil;


//--------------------------------------------------------------------------------------------------------------
// Target information address record for the sendBufferContent function.
//--------------------------------------------------------------------------------------------------------------

  _targetAddr : KSSocketAddr;


//--------------------------------------------------------------------------------------------------------------
// This is the initialization routine.
// It gets called AFTER loading the kernel and gets the handle of the shared memory passed as argument.
//--------------------------------------------------------------------------------------------------------------

function _initFunction(pArgs: PSharedData; pContext: Pointer): KSError; stdcall;
var
  error      : KSError;
  socketAddr : KSSocketAddr;
  mtu        : Integer;

begin


  //------------------------------------------------------------------------------------------------------------
  // The pointer to the shared memory gets passed as the "pArgs" parameter by KS_execKernelFunctionEx().
  //------------------------------------------------------------------------------------------------------------

  _pSys := pArgs;


  //------------------------------------------------------------------------------------------------------------
  // Now the network adapter will be opened.
  // The name is a part of the device name of the adapter.
  // "DEV_1229" would work, if there is just one Intel8255x in the system.
  //------------------------------------------------------------------------------------------------------------

  error := KS_openNetworkAdapter(
             @_pSys.hAdapter,                           // Address to write the new handle of the adapter to
             _pSys.pDeviceName,                         // Device name of the adapter
             nil,                                       // Adapter config (optional)
             KSF_NO_FLAGS);                             // Flags, here none
  if error <> KS_OK then
  begin
    _initFunction := error;
    exit;
  end;


  //------------------------------------------------------------------------------------------------------------
  // Now the network will be configured to use the IP address, subnet and gateway that got specified in the
  // application in userspace.
  //------------------------------------------------------------------------------------------------------------

  error := KS_execNetworkCommand(
             _pSys.hAdapter,                            // Adapter handle
             KS_NETWORK_SET_IP_CONFIG,                  // Command
             @_pSys.ipConfig,                           // Address of KSIPConfig record
             KSF_NO_FLAGS);                             // Flags, here none
  if error <> KS_OK then
  begin
    _initFunction := error;
    exit;
  end;


  //------------------------------------------------------------------------------------------------------------
  // Open socket as UDP client.
  //------------------------------------------------------------------------------------------------------------

  socketAddr.family     := KS_FAMILY_IPV4;
  socketAddr.port       := KS_htons(_pSys.udpPort);
  socketAddr.address[0] := _pSys.ipConfig.localAddress;

  error := KS_openSocket(
             @_pSys.hSocket,                            // Address to write the new socket handle to
             _pSys.hAdapter,                            // Adapter handle
             @socketAddr,                               // Address of KSSocketAddr record
             KS_PROTOCOL_UDP,                           // Socket protocol
             KSF_NO_FLAGS);                             // Flags, here none
  if error <> KS_OK then
  begin
    _initFunction := error;
    exit;
  end;


  //------------------------------------------------------------------------------------------------------------
  // Set the maximum transfer unit (MTU) for the socket.
  //------------------------------------------------------------------------------------------------------------

  mtu := 1024;
  error := KS_execSocketCommand(
             _pSys.hSocket,                             // Socket handle
             KS_SOCKET_SET_MTU,                         // Id identifying the command to execute
             @mtu,                                      // Address of value for the command to exceute
             KSF_NO_FLAGS);                             // Flags (unused)
  if error <> KS_OK then
  begin
    _initFunction := error;
    exit;
  end;


  //------------------------------------------------------------------------------------------------------------
  // Fill the target information address record for the sendBufferContent function.
  //------------------------------------------------------------------------------------------------------------

  _targetAddr.family     := KS_FAMILY_IPV4;
  _targetAddr.port       := KS_htons(_pSys.udpPort);
  _targetAddr.address[0] := _pSys.serverIp;

  _initFunction := KS_OK;
end;


//--------------------------------------------------------------------------------------------------------------
// This is the cleanup function that removes the socket and closes the network adapter.
//--------------------------------------------------------------------------------------------------------------

function _exitFunction(pArgs, pContext: Pointer): KSError; stdcall;
var
  error : KSError;

begin

  if _pSys = nil then                                   // Shared memory not mapped!
  begin                                                 // _initFunction not called?
    _exitFunction := KSERROR_FUNCTION_NOT_AVAILABLE;
    exit;
  end;


  //------------------------------------------------------------------------------------------------------------
  // Close the socket.
  //------------------------------------------------------------------------------------------------------------

  error := KS_closeSocket(
             _pSys.hSocket);                            // Socket handle
  if error <> KS_OK then
  begin
    _exitFunction := error;
    exit;
  end;


  //------------------------------------------------------------------------------------------------------------
  // Close the network adapter.
  //------------------------------------------------------------------------------------------------------------

  error := KS_closeNetwork(
             _pSys.hAdapter,                            // Adapter handle
             KSF_NO_FLAGS);                             // Flags (unused)
  if error <> KS_OK then
  begin
    _exitFunction := error;
    exit;
  end;

  _exitFunction := KS_OK;
end;


//--------------------------------------------------------------------------------------------------------------
// _sendBufferContent() will get called from the application in userspace and will transmit the content of the
// SharedData.pSendBuffer through the socket to the connected target.
//--------------------------------------------------------------------------------------------------------------

function _sendBufferContent(pArgs, pContext: Pointer): KSError; stdcall;
const
  endline : array [0..1] of AnsiChar = Chr(13) + Chr(10);

var
  error : KSError;

begin

  if _pSys.sendLength <> 0 then
  begin
    error := KS_sendToSocket(
               _pSys.hSocket,                           // Socket handle
               @_targetAddr,                            // Address of KSSocketAddr record for target
               @_pSys.pSendBuffer,                      // Address of buffer containing the data to send
               _pSys.sendLength,                        // Length of data to send
               nil,                                     // Returned length of data sent (not used)
               KSF_NO_FLAGS);                           // Flags (unused)
    if error <> KS_OK then
    begin
      _sendBufferContent := error;
      exit;
    end;

    error := KS_sendToSocket(
               _pSys.hSocket,                           // Socket handle
               @_targetAddr,                            // Address of KSSocketAddr record for target
               @endline[0],                             // Send newline after the actual message
               2,                                       // Length of data to send
               nil,                                     // Returned length of data sent (not used)
               KSF_NO_FLAGS);                           // Flags (unused)
    if error <> KS_OK then
    begin
      _sendBufferContent := error;
      exit;
    end;
  end;

  _sendBufferContent := KS_OK;
end;


//--------------------------------------------------------------------------------------------------------------
// Attention!
// Please note, that all functions referenced from outside the dll should be exported.
// Otherwise their names could not be found by the loader.
//--------------------------------------------------------------------------------------------------------------

exports
  _initFunction name '_initFunction',
  _exitFunction name '_exitFunction',
  _sendBufferContent name '_sendBufferContent';

begin
end.
